/*
 * @(#)DownloadResponse.java 1.8 07/03/15 Copyright (c) 2006 Sun Microsystems,
 * Inc. All Rights Reserved. Redistribution and use in source and binary forms,
 * with or without modification, are permitted provided that the following
 * conditions are met: -Redistribution of source code must retain the above
 * copyright notice, this list of conditions and the following disclaimer.
 * -Redistribution in binary form must reproduce the above copyright notice,
 * this list of conditions and the following disclaimer in the documentation
 * and/or other materials provided with the distribution. Neither the name of
 * Sun Microsystems, Inc. or the names of contributors may be used to endorse or
 * promote products derived from this software without specific prior written
 * permission. This software is provided "AS IS," without a warranty of any
 * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES,
 * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
 * PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN MIDROSYSTEMS, INC.
 * ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY
 * LICENSEE AS A RESULT OF USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
 * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR ANY LOST
 * REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL,
 * INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY
 * OF LIABILITY, ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE,
 * EVEN IF SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. You
 * acknowledge that this software is not designed, licensed or intended for use
 * in the design, construction, operation or maintenance of any nuclear
 * facility.
 */

package jnlp.sample.servlet;

import java.io.*;
import java.util.*;
import java.net.URL;
import java.net.URLConnection;
import javax.servlet.http.HttpServletResponse;

/**
 * A class used to encapsulate a file response, and factory methods to create
 * some common types.
 */
abstract public class DownloadResponse
{
    private static final String HEADER_LASTMOD = "Last-Modified";

    private static final String HEADER_JNLP_VERSION = "x-java-jnlp-version-id";

    private static final String JNLP_ERROR_MIMETYPE = "application/x-java-jnlp-error";

    public static final int STS_00_OK = 0;

    public static final int ERR_10_NO_RESOURCE = 10;

    public static final int ERR_11_NO_VERSION = 11;

    public static final int ERR_20_UNSUP_OS = 20;

    public static final int ERR_21_UNSUP_ARCH = 21;

    public static final int ERR_22_UNSUP_LOCALE = 22;

    public static final int ERR_23_UNSUP_JRE = 23;

    public static final int ERR_99_UNKNOWN = 99;

    // HTTP Compression RFC 2616 : Standard headers
    public static final String CONTENT_ENCODING = "content-encoding";

    // HTTP Compression RFC 2616 : Standard header for HTTP/Pack200 Compression
    public static final String GZIP_ENCODING = "gzip";

    public static final String PACK200_GZIP_ENCODING = "pack200-gzip";

    public DownloadResponse()
    { /* do nothing */
    }

    public String toString()
    {
	return getClass().getName();
    }

    /** Post information to an HttpResponse */
    abstract void sendRespond(HttpServletResponse response) throws IOException;

    /** Factory methods for error responses */
    static DownloadResponse getNotFoundResponse()
    {
	return new NotFoundResponse();
    }

    static DownloadResponse getNoContentResponse()
    {
	return new NotFoundResponse();
    }

    static DownloadResponse getJnlpErrorResponse(int jnlpErrorCode)
    {
	return new JnlpErrorResponse(jnlpErrorCode);
    }

    /** Factory method for file download responses */

    static DownloadResponse getNotModifiedResponse()
    {
	return new NotModifiedResponse();
    }

    static DownloadResponse getHeadRequestResponse(String mimeType, String versionId,
	    long lastModified, int contentLength)
    {
	return new HeadRequestResponse(mimeType, versionId, lastModified, contentLength);
    }

    static DownloadResponse getFileDownloadResponse(byte[] content, String mimeType,
	    long timestamp, String versionId)
    {
	return new ByteArrayFileDownloadResponse(content, mimeType, versionId, timestamp);
    }

    static DownloadResponse getFileDownloadResponse(URL resource, String mimeType, long timestamp,
	    String versionId)
    {
	return new ResourceFileDownloadResponse(resource, mimeType, versionId, timestamp);
    }

    static DownloadResponse getFileDownloadResponse(File file, String mimeType, long timestamp,
	    String versionId)
    {
	return new DiskFileDownloadResponse(file, mimeType, versionId, timestamp);
    }

    //
    // Private classes implementing the various types
    //

    static private class NotModifiedResponse extends DownloadResponse
    {
	public void sendRespond(HttpServletResponse response) throws IOException
	{
	    response.sendError(HttpServletResponse.SC_NOT_MODIFIED);
	}
    }

    static private class NotFoundResponse extends DownloadResponse
    {
	public void sendRespond(HttpServletResponse response) throws IOException
	{
	    response.sendError(HttpServletResponse.SC_NOT_FOUND);
	}
    }

    static private class NoContentResponse extends DownloadResponse
    {
	public void sendRespond(HttpServletResponse response) throws IOException
	{
	    response.sendError(HttpServletResponse.SC_NO_CONTENT);
	}
    }

    static private class HeadRequestResponse extends DownloadResponse
    {
	private String _mimeType;

	private String _versionId;

	private long _lastModified;

	private int _contentLength;

	HeadRequestResponse(String mimeType, String versionId, long lastModified, int contentLength)
	{
	    _mimeType = mimeType;
	    _versionId = versionId;
	    _lastModified = lastModified;
	    _contentLength = contentLength;
	}

	/** Post information to an HttpResponse */
	public void sendRespond(HttpServletResponse response) throws IOException
	{
	    // Set header information
	    response.setContentType(_mimeType);
	    response.setContentLength(_contentLength);
	    if (_versionId != null)
	    {
		response.setHeader(HEADER_JNLP_VERSION, _versionId);
	    }
	    if (_lastModified != 0)
	    {
		response.setDateHeader(HEADER_LASTMOD, _lastModified);
	    }
	    response.sendError(HttpServletResponse.SC_OK);
	}
    }

    static public class JnlpErrorResponse extends DownloadResponse
    {
	private String _message;

	public JnlpErrorResponse(int jnlpErrorCode)
	{
	    String msg = Integer.toString(jnlpErrorCode);
	    String dsc = "No description";
	    try
	    {
		dsc = JnlpDownloadServlet.getResourceBundle().getString("servlet.jnlp.err." + msg);
	    }
	    catch (MissingResourceException mre)
	    { /* ignore */
	    }
	    _message = msg + " " + dsc;
	}

	public void sendRespond(HttpServletResponse response) throws IOException
	{
	    response.setContentType(JNLP_ERROR_MIMETYPE);
	    PrintWriter pw = response.getWriter();
	    pw.println(_message);
	};

	public String toString()
	{
	    return super.toString() + "[" + _message + "]";
	}
    }

    static private abstract class FileDownloadResponse extends DownloadResponse
    {
	private String _mimeType;

	private String _versionId;

	private long _lastModified;

	private String _fileName;

	FileDownloadResponse(String mimeType, String versionId, long lastModified)
	{
	    _mimeType = mimeType;
	    _versionId = versionId;
	    _lastModified = lastModified;
	    _fileName = null;
	}

	FileDownloadResponse(String mimeType, String versionId, long lastModified, String fileName)
	{
	    _mimeType = mimeType;
	    _versionId = versionId;
	    _lastModified = lastModified;
	    _fileName = fileName;
	}

	/** Information about response */
	String getMimeType()
	{
	    return _mimeType;
	}

	String getVersionId()
	{
	    return _versionId;
	}

	long getLastModified()
	{
	    return _lastModified;
	}

	abstract int getContentLength() throws IOException;

	abstract InputStream getContent() throws IOException;

	/** Post information to an HttpResponse */
	public void sendRespond(HttpServletResponse response) throws IOException
	{
	    // Set header information
	    response.setContentType(getMimeType());
	    response.setContentLength(getContentLength());
	    if (getVersionId() != null)
		response.setHeader(HEADER_JNLP_VERSION, getVersionId());
	    if (getLastModified() != 0)
		response.setDateHeader(HEADER_LASTMOD, getLastModified());
	    if (_fileName != null)
	    {

		if (_fileName.endsWith(".pack.gz"))
		{
		    response.setHeader(CONTENT_ENCODING, PACK200_GZIP_ENCODING);
		}
		else if (_fileName.endsWith(".gz"))
		{
		    response.setHeader(CONTENT_ENCODING, GZIP_ENCODING);
		}
		else
		{
		    response.setHeader(CONTENT_ENCODING, null);
		}
	    }

	    // Send contents
	    InputStream in = getContent();
	    OutputStream out = response.getOutputStream();
	    try
	    {
		byte[] bytes = new byte[32 * 1024];
		int read;
		while ((read = in.read(bytes)) != -1)
		{
		    out.write(bytes, 0, read);
		}
	    }
	    finally
	    {
		if (in != null)
		    in.close();
	    }
	}

	protected String getArgString()
	{
	    long length = 0;
	    try
	    {
		length = getContentLength();
	    }
	    catch (IOException ioe)
	    { /* ignore */
	    }
	    return "Mimetype=" + getMimeType() + " VersionId=" + getVersionId() + " Timestamp="
		    + new Date(getLastModified()) + " Length=" + length;
	}
    }

    static private class ByteArrayFileDownloadResponse extends FileDownloadResponse
    {
	private byte[] _content;

	ByteArrayFileDownloadResponse(byte[] content, String mimeType, String versionId,
		long lastModified)
	{
	    super(mimeType, versionId, lastModified);
	    _content = content;
	}

	int getContentLength()
	{
	    return _content.length;
	}

	InputStream getContent()
	{
	    return new ByteArrayInputStream(_content);
	}

	public String toString()
	{
	    return super.toString() + "[ " + getArgString() + "]";
	}
    }

    static private class ResourceFileDownloadResponse extends FileDownloadResponse
    {
	URL _url;

	ResourceFileDownloadResponse(URL url, String mimeType, String versionId, long lastModified)
	{
	    super(mimeType, versionId, lastModified, url.toString());
	    _url = url;
	}

	int getContentLength() throws IOException
	{
	    return _url.openConnection().getContentLength();
	}

	InputStream getContent() throws IOException
	{
	    return _url.openConnection().getInputStream();
	}

	public String toString()
	{
	    return super.toString() + "[ " + getArgString() + "]";
	}
    }

    static private class DiskFileDownloadResponse extends FileDownloadResponse
    {
	private File _file;

	DiskFileDownloadResponse(File file, String mimeType, String versionId, long lastModified)
	{
	    super(mimeType, versionId, lastModified, file.getName());
	    _file = file;
	}

	int getContentLength() throws IOException
	{
	    return (int) _file.length();
	}

	InputStream getContent() throws IOException
	{
	    return new BufferedInputStream(new FileInputStream(_file));
	}

	public String toString()
	{
	    return super.toString() + "[ " + getArgString() + "]";
	}
    }
}
